package biz

import (
	"context"
	"crypto/md5"
	"crypto/sha256"
	"encoding/hex"
	"errors"
	"fmt"
	v1 "gitlab.cqcb.com/shangyou_mic/dscm-approve-api-pb/api/roses/v1"
	"rosesbff/internal/data/redis"
	"strings"
	"sync"
	"time"

	"rosesbff/pkg/util"

	"github.com/golang-jwt/jwt/v5"
)

const (
	// SystemRoleReviewer 复核员
	SystemRoleReviewer = "reviewer"

	// SystemRoleReader 审读员
	SystemRoleReader = "reader"
)

type AccountRepo interface {
	ServerGetUser(ctx context.Context, req *v1.FxUser) (*v1.FxUser, error)
	ServerSaveUser(ctx context.Context, req *v1.FxUser) (*v1.FxUser, error)
	ServerSystemRole(ctx context.Context, req *v1.ServerSystemRoleRequest) (*v1.ServerSystemRoleReply, error)
	ServerGetUserPermitLists(ctx context.Context, req *v1.FxUser) (*v1.UserPermit, error)

	CreateAccount(ctx context.Context, req *v1.CreateAccountRequest) (*v1.CreateAccountReply, error)
	GetAccountInfo(ctx context.Context, req *v1.GetAccountInfoRequest) (*v1.GetAccountInfoReply, error)
	UpdateAccountPassword(ctx context.Context, req *v1.UpdateAccountPasswordRequest) (*v1.UpdateAccountPasswordReply, error)

	CreateRbacPermit(ctx context.Context, req *v1.CreateRbacPermitRequest) (*v1.CreateRbacPermitReply, error)
	DeleteRbacPermit(ctx context.Context, req *v1.DeleteRbacPermitRequest) (*v1.DeleteRbacPermitReply, error)
	UpdateRbacPermit(ctx context.Context, req *v1.UpdateRbacPermitRequest) (*v1.UpdateRbacPermitReply, error)
	ListsRbacPermit(ctx context.Context, req *v1.ListsRbacPermitRequest) (*v1.ListsRbacPermitReply, error)

	CreateRbacRole(ctx context.Context, req *v1.CreateRbacRoleRequest) (*v1.CreateRbacRoleReply, error)
	DeleteRbacRole(ctx context.Context, req *v1.DeleteRbacRoleRequest) (*v1.DeleteRbacRoleReply, error)
	UpdateRbacRole(ctx context.Context, req *v1.UpdateRbacRoleRequest) (*v1.UpdateRbacRoleReply, error)
	ListsRbacRole(ctx context.Context, req *v1.ListsRbacRoleRequest) (*v1.ListsRbacRoleReply, error)

	RbacUserList(ctx context.Context, req *v1.RbacUserListRequest) (*v1.RbacUserListReply, error)
	RbacPermitList(ctx context.Context, req *v1.RbacPermitListRequest) (*v1.RbacPermitListReply, error)

	ListsUser(ctx context.Context, req *v1.ListsUserRequest) (*v1.ListsUserReply, error)
	UpdateUser(ctx context.Context, req *v1.UpdateUserRequest) (*v1.UpdateUserReply, error)
	DeleteUser(ctx context.Context, req *v1.DeleteUserRequest) (*v1.DeleteUserReply, error)

	CreateSystemNotice(ctx context.Context, req *v1.CreateSystemNoticeRequest) (*v1.Response, error)
	DeleteSystemNotice(ctx context.Context, req *v1.DeleteSystemNoticeRequest) (*v1.Response, error)
	UpdateSystemNotice(ctx context.Context, req *v1.UpdateSystemNoticeRequest) (*v1.Response, error)
	SelectSystemNotice(ctx context.Context, req *v1.SelectSystemNoticeRequest) (*v1.SelectSystemNoticeReply, error)

	UserMessageUnread(ctx context.Context, req *v1.UserMessageUnreadRequest) (*v1.UserMessageUnreadReply, error)
	UserMessageLists(ctx context.Context, req *v1.UserMessageListsRequest) (*v1.UserMessageListsReply, error)
	UserMessageRead(ctx context.Context, req *v1.UserMessageReadRequest) (*v1.UserMessageReadReply, error)

	ListsReader(ctx context.Context, req *v1.ListsReaderRequest) (*v1.ListsReaderReply, error)
	UpdateReader(ctx context.Context, req *v1.UpdateReaderRequest) (*v1.UpdateReaderReply, error)

	ReaderList(ctx context.Context, req *v1.ReaderListRequest) (*v1.ReaderListReply, error)

	ApproveSourcesLists(ctx context.Context, req *v1.ApproveSourcesListsRequest) (*v1.ApproveSourcesListsReply, error)
	ApproveSourcesUpdate(ctx context.Context, req *v1.ApproveSourcesUpdateRequest) (*v1.ApproveSourcesUpdateReply, error)
	ApproveSourcesUpdateReader(ctx context.Context, req *v1.ApproveSourcesUpdateReaderRequest) (*v1.ApproveSourcesUpdateReaderReply, error)
	ApproveSourcesItems(ctx context.Context, req *v1.ApproveSourcesItemsRequest) (*v1.ApproveSourcesItemsReply, error)

	ApproveSourcesAll(ctx context.Context, req *v1.ApproveSourcesAllRequest) (*v1.ApproveSourcesAllReply, error)
}

type CacheUserPermit struct {
	UserId      int64                // 用户id
	PermitLists []*v1.UserPermitItem // 用户权限列表
	PermitMaps  map[string]*struct{} // 用户权限映射集合
	ExpiredAt   int64                // 过期时间 0:永不过期
}

// permitListsToArray 树状权限数据转一维切片
func permitListsToArray(lists ...*v1.UserPermitItem) []*v1.UserPermitItem {
	result := make([]*v1.UserPermitItem, 0, 1<<5)
	for _, v := range lists {
		result = append(result, v)
		if len(v.Children) > 0 {
			result = append(result, permitListsToArray(v.Children...)...)
		}
	}
	return result
}

// permitListsToCache 构建缓存用户权限的数据结构
func permitListsToCache(userId int64, lists []*v1.UserPermitItem, expiredAt int64) *CacheUserPermit {
	result := &CacheUserPermit{
		UserId:      userId,
		PermitLists: permitListsToArray(lists...),
		PermitMaps:  make(map[string]*struct{}, 1<<5),
		ExpiredAt:   expiredAt,
	}
	for _, v := range result.PermitLists {
		key := fmt.Sprintf("%s-%s", v.Path, v.Method)
		result.PermitMaps[key] = nil
	}
	return result
}

type Account struct {
	*Basic

	// JWT 密钥
	loginSecretKey []byte

	// 所有权限(超级管理员的权限)
	adminPermit []*v1.UserPermitItem

	// 已经登录用户的权限
	loginUserPermit *sync.Map

	account AccountRepo
	redis   *redis.Redis
}

func NewAccount(
	account AccountRepo,
	basic *Basic,
	redis *redis.Redis,
) (*Account, error) {
	result := &Account{
		loginSecretKey:  []byte("123123"),
		loginUserPermit: &sync.Map{},
		account:         account,
		redis:           redis,
	}
	result.Basic = basic

	// 登录相关
	{
		basic.storeUserPermit = result.storeUserPermit
		if false {
			result.loginSecretKey = []byte(util.RandomString(32))
		}
		basic.loginSecretKey = result.loginSecretKey
	}

	err := result.init()
	if err != nil {
		return nil, err
	}
	go result.timerRemoveUserPermit()
	return result, nil
}

func (s *Account) init() error {
	ctx := context.Background()

	// 系统内置管理员
	{
		adminUsername := "admin"
		adminPassword := "123456"
		{
			m := md5.New()
			m.Write([]byte(adminPassword))
			adminPassword = strings.ToLower(hex.EncodeToString(m.Sum(nil)))
		}
		exist, err := s.account.ServerGetUser(ctx, &v1.FxUser{
			Username: &adminUsername,
		})
		if err != nil {
			return err
		}
		if exist.Id <= 0 {
			admin := &v1.CreateAccountRequest{
				Category: -1,
				Username: adminUsername,
				Mobile:   "11100000000",
				Password: &adminPassword,
			}
			createReplay, err := s.CreateAccount(ctx, admin)
			if err != nil {
				return err
			}
			if createReplay.Id <= 0 {
				return errors.New("create account(admin) failed")
			}
		}
		admin, err := s.account.ServerGetUser(ctx, &v1.FxUser{
			Username: &adminUsername,
		})
		if err != nil {
			return err
		}
		adminPermit, err := s.account.ServerGetUserPermitLists(ctx, admin)
		if err != nil {
			return err
		}
		s.adminPermit = adminPermit.Lists
	}

	// 系统内置角色
	{
		roles := &v1.ServerSystemRoleRequest{
			Lists: make([]*v1.RbacRole, 0),
		}
		roles.Lists = append(roles.Lists, &v1.RbacRole{
			Name:       "复核员",
			Identifier: SystemRoleReviewer,
			Note:       "系统内置-复核员",
			State:      1,
			SortNumber: 999,
		})
		roles.Lists = append(roles.Lists, &v1.RbacRole{
			Name:       "审读员",
			Identifier: SystemRoleReader,
			Note:       "系统内置-审读员",
			State:      1,
			SortNumber: 998,
		})
		_, err := s.account.ServerSystemRole(ctx, roles)
		if err != nil {
			return err
		}
	}

	return nil
}

// timerRemoveUserPermit 定时删除缓存中的权限数据
func (s *Account) timerRemoveUserPermit() {
	duration := time.Minute * 3
	timer := time.NewTimer(duration)
	defer timer.Stop()
	for {
		select {
		case <-timer.C:
			timestamp := time.Now().Unix()
			s.loginUserPermit.Range(func(k, v interface{}) bool {
				w, ok := v.(*CacheUserPermit)
				if !ok {
					s.loginUserPermit.Delete(k)
				} else {
					if w != nil {
						if w.ExpiredAt > 0 && timestamp > w.ExpiredAt {
							s.loginUserPermit.Delete(k)
						}
					}
				}
				return false
			})
			timer.Reset(duration)
		}
	}
}

type CustomClaims struct {
	UserId   int64  `json:"sub"`
	Username string `json:"name"`
	Mobile   string `json:"mobile"`
	Category int32  `json:"category"`
	jwt.RegisteredClaims
	OriginUserId int64 `json:"origin_user_id"`
}

// storeUserPermit 存储用户权限
func (s *Account) storeUserPermit(userId int64, lists []*v1.UserPermitItem, expiredAt int64) {
	cache := permitListsToCache(userId, lists, expiredAt)
	s.loginUserPermit.Store(userId, cache)
}

// Login 登录
func (s *Account) Login(ctx context.Context, req *v1.LoginRequest) (*v1.LoginReply, error) {
	result := &v1.LoginReply{}
	mobile, username := "", ""
	var user *v1.FxUser
	var err error
	switch req.Type {
	case 1:
		username = req.Username
	case 2:
		mobile = req.Username
		msg, err := s.Basic.VerifyMobileCode(ctx, req.GetScene(), mobile, req.Password)
		if err != nil {
			return nil, err
		}
		if msg != nil {
			result.StatusCode = 500
			result.Message = msg.Error()
			return result, nil
		}
	case 3: // 复核员登录 根据客户端传入的code值到甲方获取用户信息并作登录逻辑校验
		res, err := s.ssoGetUserInfo(ctx, &SsoGetUserInfoReq{TemporaryCode: req.Username})
		if err != nil {
			return nil, err
		}
		data := res.Data
		user = &v1.FxUser{
			Mobile:   &data.Userinfo.Phone,
			Email:    &data.Userinfo.Email,
			RealName: data.Userinfo.RealName,
			Note:     data.Userinfo.Name,
			UserId:   data.Userinfo.Id,
		}
		user, err = s.account.ServerSaveUser(ctx, user)
		if err != nil {
			return nil, err
		}
	}
	if user == nil {
		user, err = s.account.ServerGetUser(ctx, &v1.FxUser{
			Username: &username,
			Mobile:   &mobile,
		})
		if err != nil {
			return nil, err
		}
	}
	if user.Id <= 0 {
		return nil, errors.New("account is not found")
	}
	if req.Type == 1 {
		if !s.VerifyUserPassword(user.PasswordSalt, req.Password, user.Password) {
			result.StatusCode = 409
			result.Message = "密码错误"
			return result, nil
		}
	}
	if user.State != 1 {
		result.StatusCode = 409
		result.Message = "账户状态异常"
		return result, nil
	}

	token, err := s.BuildToken(ctx, user)
	if err != nil {
		result.StatusCode = 500
		result.Message = err.Error()
		return result, nil
	}

	result.StatusCode = 200
	result.Message = "登录成功"
	result.Data = &v1.LoginReply_Reply{
		Token:       token.Token,
		PermitLists: token.UserPermit,
	}
	return result, nil
}

// VerifyLoginToken 验证token
func (s *Account) VerifyLoginToken(ctx context.Context, token string) (*CustomClaims, error) {
	result := &CustomClaims{}
	parsedToken, err := jwt.ParseWithClaims(token, result, func(token *jwt.Token) (interface{}, error) {
		if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
			return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
		}
		return s.loginSecretKey, nil
	})
	if err != nil {
		return nil, err
	}
	// 提取 Claims 并验证
	if claims, ok := parsedToken.Claims.(*CustomClaims); ok && parsedToken.Valid {
		return claims, nil
	}
	// token 过期
	return &CustomClaims{}, nil
}

// VerifyUserRbacPermit 校验登录用户是否存在对应的权限
func (s *Account) VerifyUserRbacPermit(userId int64, method string, urlPath string) bool {
	cached, ok := s.loginUserPermit.Load(userId)
	if !ok || cached == nil {
		return false
	}
	value, ok := cached.(*CacheUserPermit)
	if !ok || value == nil {
		return false
	}
	key := fmt.Sprintf("%s-%s", urlPath, method)
	_, ok = value.PermitMaps[key]
	return ok
}

// EncryptUserPassword 加密用户密码
func (s *Account) EncryptUserPassword(passwordSalt, inputPassword string) string {
	hash := sha256.New()
	hash.Write([]byte(fmt.Sprintf("%s_%s", passwordSalt, inputPassword)))
	return hex.EncodeToString(hash.Sum(nil))
}

// VerifyUserPassword 校验用户密码
func (s *Account) VerifyUserPassword(passwordSalt, inputPassword, starePassword string) bool {
	hash := sha256.New()
	hash.Write([]byte(fmt.Sprintf("%s_%s", passwordSalt, inputPassword)))
	password := hex.EncodeToString(hash.Sum(nil))
	return starePassword == password
}

func (s *Account) CreateAccount(ctx context.Context, req *v1.CreateAccountRequest) (*v1.CreateAccountReply, error) {
	if req.Password != nil {
		passwordSalt := req.GetPasswordSalt()
		if passwordSalt == "" {
			passwordSalt = util.RandomString(8, []byte(fmt.Sprintf("%s%s", util.EnglishLetter, util.Number))...)
			req.PasswordSalt = &passwordSalt
		}
		password := s.EncryptUserPassword(passwordSalt, *req.Password)
		req.Password = &password
	}
	return s.account.CreateAccount(ctx, req)
}

func (s *Account) GetAccountInfo(ctx context.Context, req *v1.GetAccountInfoRequest) (*v1.GetAccountInfoReply, error) {
	return s.account.GetAccountInfo(ctx, req)
}

func (s *Account) UpdateAccountPassword(ctx context.Context, req *v1.UpdateAccountPasswordRequest) (*v1.UpdateAccountPasswordReply, error) {
	userId := s.GetUserId(ctx)
	req.UserId = &userId
	// 验证手机验证码是否正确
	msg, err := s.Basic.VerifyMobileCode(ctx, req.Scene, req.Mobile, req.VerifyCode)
	if err != nil {
		return nil, err
	}
	reply := &v1.UpdateAccountPasswordReply{}
	if msg != nil {
		reply.StatusCode = 500
		reply.Message = msg.Error()
		return reply, nil
	}
	user, err := s.account.ServerGetUser(ctx, &v1.FxUser{
		Id: userId,
	})
	if err != nil {
		return nil, err
	}
	password := s.EncryptUserPassword(user.PasswordSalt, req.NewPassword)
	if password == user.Password {
		reply.StatusCode = 500
		reply.Message = "新密码不能与旧密码相同"
		return reply, nil
	}
	req.NewPassword = password
	return s.account.UpdateAccountPassword(ctx, req)
}

func (s *Account) CreateRbacPermit(ctx context.Context, req *v1.CreateRbacPermitRequest) (*v1.CreateRbacPermitReply, error) {
	return s.account.CreateRbacPermit(ctx, req)
}

func (s *Account) DeleteRbacPermit(ctx context.Context, req *v1.DeleteRbacPermitRequest) (*v1.DeleteRbacPermitReply, error) {
	return s.account.DeleteRbacPermit(ctx, req)
}

func (s *Account) UpdateRbacPermit(ctx context.Context, req *v1.UpdateRbacPermitRequest) (*v1.UpdateRbacPermitReply, error) {
	return s.account.UpdateRbacPermit(ctx, req)
}

func (s *Account) ListsRbacPermit(ctx context.Context, req *v1.ListsRbacPermitRequest) (*v1.ListsRbacPermitReply, error) {
	return s.account.ListsRbacPermit(ctx, req)
}

func (s *Account) CreateRbacRole(ctx context.Context, req *v1.CreateRbacRoleRequest) (*v1.CreateRbacRoleReply, error) {
	return s.account.CreateRbacRole(ctx, req)
}

func (s *Account) DeleteRbacRole(ctx context.Context, req *v1.DeleteRbacRoleRequest) (*v1.DeleteRbacRoleReply, error) {
	return s.account.DeleteRbacRole(ctx, req)
}

func (s *Account) UpdateRbacRole(ctx context.Context, req *v1.UpdateRbacRoleRequest) (*v1.UpdateRbacRoleReply, error) {
	return s.account.UpdateRbacRole(ctx, req)
}

func (s *Account) ListsRbacRole(ctx context.Context, req *v1.ListsRbacRoleRequest) (*v1.ListsRbacRoleReply, error) {
	return s.account.ListsRbacRole(ctx, req)
}

func (s *Account) RbacUserList(ctx context.Context, req *v1.RbacUserListRequest) (*v1.RbacUserListReply, error) {
	return s.account.RbacUserList(ctx, req)
}

func (s *Account) RbacPermitList(ctx context.Context, req *v1.RbacPermitListRequest) (*v1.RbacPermitListReply, error) {
	return s.account.RbacPermitList(ctx, req)
}

func (s *Account) ListsUser(ctx context.Context, req *v1.ListsUserRequest) (*v1.ListsUserReply, error) {
	return s.account.ListsUser(ctx, req)
}

func (s *Account) UpdateUser(ctx context.Context, req *v1.UpdateUserRequest) (*v1.UpdateUserReply, error) {
	if req.Password != nil {
		user, err := s.account.ServerGetUser(ctx, &v1.FxUser{
			Id: req.Id,
		})
		if err != nil {
			return nil, err
		}
		if user.Id <= 0 {
			return nil, errors.New("account is not found")
		}
		password := s.EncryptUserPassword(user.PasswordSalt, *req.Password)
		req.Password = &password
	}
	return s.account.UpdateUser(ctx, req)
}

func (s *Account) DeleteUser(ctx context.Context, req *v1.DeleteUserRequest) (*v1.DeleteUserReply, error) {
	return s.account.DeleteUser(ctx, req)
}

func (s *Account) CreateSystemNotice(ctx context.Context, req *v1.CreateSystemNoticeRequest) (*v1.Response, error) {
	return s.account.CreateSystemNotice(ctx, req)
}

func (s *Account) DeleteSystemNotice(ctx context.Context, req *v1.DeleteSystemNoticeRequest) (*v1.Response, error) {
	return s.account.DeleteSystemNotice(ctx, req)
}

func (s *Account) UpdateSystemNotice(ctx context.Context, req *v1.UpdateSystemNoticeRequest) (*v1.Response, error) {
	userId := s.GetUserId(ctx)
	req.UserId = &userId
	return s.account.UpdateSystemNotice(ctx, req)
}

func (s *Account) SelectSystemNotice(ctx context.Context, req *v1.SelectSystemNoticeRequest) (*v1.SelectSystemNoticeReply, error) {
	return s.account.SelectSystemNotice(ctx, req)
}

func (s *Account) UserMessageUnread(ctx context.Context, req *v1.UserMessageUnreadRequest) (*v1.UserMessageUnreadReply, error) {
	userId := s.GetUserId(ctx)
	req.UserId = &userId
	return s.account.UserMessageUnread(ctx, req)
}

func (s *Account) UserMessageLists(ctx context.Context, req *v1.UserMessageListsRequest) (*v1.UserMessageListsReply, error) {
	userId := s.GetUserId(ctx)
	req.UserId = &userId
	return s.account.UserMessageLists(ctx, req)
}

func (s *Account) UserMessageRead(ctx context.Context, req *v1.UserMessageReadRequest) (*v1.UserMessageReadReply, error) {
	userId := s.GetUserId(ctx)
	req.UserId = &userId
	return s.account.UserMessageRead(ctx, req)
}

func (s *Account) ListsReader(ctx context.Context, req *v1.ListsReaderRequest) (*v1.ListsReaderReply, error) {
	return s.account.ListsReader(ctx, req)
}

func (s *Account) UpdateReader(ctx context.Context, req *v1.UpdateReaderRequest) (*v1.UpdateReaderReply, error) {
	return s.account.UpdateReader(ctx, req)
}

func (s *Account) ReaderList(ctx context.Context, req *v1.ReaderListRequest) (*v1.ReaderListReply, error) {
	return s.account.ReaderList(ctx, req)
}

func (s *Account) ApproveSourcesLists(ctx context.Context, req *v1.ApproveSourcesListsRequest) (*v1.ApproveSourcesListsReply, error) {
	return s.account.ApproveSourcesLists(ctx, req)
}

func (s *Account) ApproveSourcesUpdate(ctx context.Context, req *v1.ApproveSourcesUpdateRequest) (*v1.ApproveSourcesUpdateReply, error) {
	return s.account.ApproveSourcesUpdate(ctx, req)
}

func (s *Account) ApproveSourcesUpdateReader(ctx context.Context, req *v1.ApproveSourcesUpdateReaderRequest) (*v1.ApproveSourcesUpdateReaderReply, error) {
	return s.account.ApproveSourcesUpdateReader(ctx, req)
}

func (s *Account) ApproveSourcesItems(ctx context.Context, req *v1.ApproveSourcesItemsRequest) (*v1.ApproveSourcesItemsReply, error) {
	return s.account.ApproveSourcesItems(ctx, req)
}

func (s *Account) ApproveSourcesAll(ctx context.Context, req *v1.ApproveSourcesAllRequest) (*v1.ApproveSourcesAllReply, error) {
	return s.account.ApproveSourcesAll(ctx, req)
}
